#
# Copyright 2017, Data61
# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
# ABN 41 687 119 230.
#
# This software may be distributed and modified according to the terms of
# the BSD 2-Clause license. Note that NO WARRANTY is provided.
# See "LICENSE_BSD2.txt" for details.
#
# @TAG(DATA61_BSD)
#

cmake_minimum_required(VERSION 3.20.0)

set(MUSLLIBC_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(MUSLLIBC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})

project(libmuslc C ASM)

# Map ARCH to muslc architecture.
set(
  arch_mapping # element format: "ARCH[:muslc_arch]"
  "ia32:i386"
  "x86_64"
  "arm"
  "arm64:aarch64"
  "riscv32:riscv"
  "riscv64:riscv"
)
# Find the tuple with the mapping information. If ARCH is not set at
# all, matching will fail.
if(NOT ";${arch_mapping};" MATCHES ";${ARCH}(:([^;]*))?;")
  message(FATAL_ERROR "unsupported ARCH: '${ARCH}'")
elseif(CMAKE_MATCH_2)
  set(muslc_arch "${CMAKE_MATCH_2}")
else()
  set(muslc_arch "${ARCH}")
endif()

message(
    STATUS "${PROJECT_NAME} architecture: '${muslc_arch}' (from ARCH '${ARCH}')"
)

if(CONFIG_MUSLC_GLOB_SOURCES)
  file(
    GLOB_RECURSE
      deps
      **/*.c
      **/*.h
      **/*.S
      **/*.s
  )
endif()

set(parallel "")
if(CONFIG_MUSLC_PARALLEL_BUILD)
  include(ProcessorCount)
  ProcessorCount(N)
  if(N EQUAL 0)
    message(WARNING "Could not detect ProcessorCount for building musllibc.")
  else()
    # We set tasks to number of CPUs + 2 to account for blocking IO, this is
    # a similar heuristic to what Ninja uses by default. Note that this gets
    # used in a rule generated by add_custom_command and is additive with
    # any existing Ninja jobs.
    math(EXPR Nplus2 "${N} + 2")
    set(parallel "-j${Nplus2}")
  endif()
endif()

# We make an attempt to extract compiler options from CMake
get_property(compile_options DIRECTORY PROPERTY COMPILE_OPTIONS)

list(APPEND compile_options "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}")

if(CONFIG_MUSLC_DEBUG_BUILD)
  set(extra_options ${compile_options}"--enable-debug")
endif()
set(extra_options ${extra_options}${muslc_arch})
set(extra_options ${extra_options}${deps})

include(memoize)
# memoize this installation rule which will save the resulting artifact in a
# cache and reuse it across builds. This will rebuild from source if the git
# directory has changes or has a changed commit hash.
memoize_add_custom_command(
  musllibc
  "${CMAKE_CURRENT_BINARY_DIR}/build-temp/stage"
  "${CMAKE_CURRENT_SOURCE_DIR}"
  "${extra_options} ${muslc_arch}"
  ""
  OUTPUT
  build-temp/stage/lib/libc.a
  # If we have to rebuild, first clear the temporary build directory as we
  # have no correctly captured the output files or dependencies
  COMMAND
  rm
  -rf
  build-temp
  COMMAND
  mkdir
  -p
  build-temp
  COMMAND
  ${CMAKE_COMMAND}
  -E
  env
  TOOLPREFIX=${CROSS_COMPILE}
  C_COMPILER=${CMAKE_C_COMPILER}
  # Suppress make output by default. Verbose output can now be achieved by
  # "MAKEFLAGS=V3 ninja". "${MAKEFLAGS:--s}" expands to "-s" if MAKEFLAGS is
  # unset.
  "MAKEFLAGS=\$\${MAKEFLAGS:--s}"
  # TODO: should also pass in the cflags for the CMAKE_BUILD_TYPE ?
  NK_CFLAGS="${compile_options}"
  TARGET=${muslc_arch}
  make
  -C
  build-temp
  ${parallel}
  -f
  "${CMAKE_CURRENT_SOURCE_DIR}/Makefile"
  "STAGE_DIR=${CMAKE_CURRENT_BINARY_DIR}/build-temp/stage"
  "SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/src"
  "CONFIG_MUSLC_DEBUG_BUILD=${CONFIG_MUSLC_DEBUG_BUILD}"
  "muslc_arch=${muslc_arch}"
  DEPENDS
  ${deps}
  COMMAND_EXPAND_LISTS
  COMMENT
  "Invoking muslc build system"
)

add_custom_target(muslc_gen DEPENDS build-temp/stage/lib/libc.a)

add_library(muslc_imported STATIC IMPORTED GLOBAL)
add_dependencies(muslc_imported muslc_gen)
set_property(
  TARGET muslc_imported
  PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/build-temp/stage/lib/libc.a"
)

add_library(musllibc INTERFACE)
add_dependencies(musllibc muslc_imported)
set_property(TARGET musllibc PROPERTY INTERFACE_LINK_LIBRARIES muslc_imported)
set_property(GLOBAL PROPERTY APP_CRUNTIME_DIR ${CMAKE_CURRENT_BINARY_DIR}/build-temp/stage/lib)
target_include_directories(musllibc INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/build-temp/stage/include")

add_library(musllibc_interface INTERFACE)
add_dependencies(musllibc_interface muslc_imported)
target_include_directories(musllibc_interface INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/build-temp/stage/include")
